﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using GurLoader.GlobalHotkey;
using NUnit.Framework;

namespace GurLoaderWPF.Test.GlobalHotKey
{
    [TestFixture]
    public class HotKeyTests
    {
        [Test]
        public void CreateInstanceDefaultTest()
        {
            HotKey hk = new HotKey();

            Assert.IsNotNull(hk);
        }

        [Test]
        public void CreateInstanceKeyModifiersTest()
        {
            Key key = Key.C;
            ModifierKeys modifierKeys = ModifierKeys.Alt | ModifierKeys.Control;
            
            HotKey hk = new HotKey(key, modifierKeys);

            Assert.IsNotNull(hk);

            Assert.AreEqual(key, hk.Key);
            Assert.AreEqual(modifierKeys, hk.Modifiers);
        }

        [Test]
        public void CreateInstanceKeyModifiersEnabledTest()
        {
            Key key = Key.C;
            ModifierKeys modifierKeys = ModifierKeys.Alt | ModifierKeys.Control;
            bool enabled = false;

            HotKey hk = new HotKey(key, modifierKeys, enabled);

            Assert.IsNotNull(hk);

            Assert.AreEqual(key, hk.Key);
            Assert.AreEqual(modifierKeys, hk.Modifiers);
            Assert.AreEqual(enabled, hk.Enabled);
        }

        [Test]
        public void CreateInstanceKeyModifiersActionTest()
        {
            Key key = Key.C;
            ModifierKeys modifierKeys = ModifierKeys.Alt | ModifierKeys.Control;
            Action action = () =>
            {

            };

            HotKey hk = new HotKey(key, modifierKeys, action);

            Assert.IsNotNull(hk);

            Assert.AreEqual(key, hk.Key);
            Assert.AreEqual(modifierKeys, hk.Modifiers);
            Assert.AreEqual(action, hk.OnPressedAction);
        }

        [Test]
        public void CreateInstanceKeyModifiersEnabledActionTest()
        {
            Key key = Key.C;
            ModifierKeys modifierKeys = ModifierKeys.Alt | ModifierKeys.Control;
            bool enabled = false;

            Action action = () =>
            {

            };

            HotKey hk = new HotKey(key, modifierKeys, enabled, action);

            Assert.IsNotNull(hk);

            Assert.AreEqual(key, hk.Key);
            Assert.AreEqual(modifierKeys, hk.Modifiers);
            Assert.AreEqual(enabled, hk.Enabled);
            Assert.AreEqual(action, hk.OnPressedAction);
        }

        [Test]
        public void NotifyPropertyChangedHandlerTest()
        {
            HotKey hk = new HotKey();
            ManualResetEvent mre = new ManualResetEvent(false);
            hk.PropertyChanged += (sender, e) =>
            {
                mre.Set();
            };

            hk.Key = Key.D;
            bool signalled = mre.WaitOne(1000*5);

            if ( ! signalled )
                Assert.Fail("Timed out waiting for notification callback.");
        }

        [Test]
        public void EqualsWithNonHotkeyOtherTest()
        {
            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt);
            Assert.IsFalse( hk.Equals(10) );
        }

        [Test]
        public void EqualsWithEqualHotkeyTest()
        {
            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt);
            HotKey other = new HotKey(Key.C, ModifierKeys.Alt);

            Assert.IsTrue(hk.Equals((object) other));
        }

        [Test]
        public void EqualsWithNonEqualKeyTest()
        {
            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt);
            HotKey other = new HotKey(Key.F, ModifierKeys.Alt);

            Assert.IsFalse(hk.Equals((object) other));            
        }

        [Test]
        public void EqualsWithNonEqualModifiersTest()
        {
            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt);
            HotKey other = new HotKey(Key.C, ModifierKeys.Shift);

            Assert.IsFalse(hk.Equals((object) other));
        }

        [Test]
        public void ToStringTest()
        {
            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt);
            string result = hk.ToString();

            Assert.IsNotNullOrEmpty(result);

            Assert.IsTrue(result.Length > 2);
        }

        [Test]
        public void HashCodeTest()
        {
            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt);
            int code = hk.GetHashCode();

            HotKey other = new HotKey(Key.C, ModifierKeys.Control);
            int otherCode = other.GetHashCode();

            Assert.AreNotEqual(code, otherCode);
        }

        #region private derived hk class
        private class DerivedHotKey : HotKey
        {
            public DerivedHotKey()
            {
            }

            public DerivedHotKey(Key key, ModifierKeys modifiers) : base(key, modifiers)
            {
            }

            public DerivedHotKey(Key key, ModifierKeys modifiers, bool enabled) : base(key, modifiers, enabled)
            {
            }

            public DerivedHotKey(Key key, ModifierKeys modifiers, Action action) : base(key, modifiers, action)
            {
            }

            public DerivedHotKey(Key key, ModifierKeys modifiers, bool enabled, Action action) : base(key, modifiers, enabled, action)
            {
            }

            public void RaiseHKPress()
            {
                RaiseOnHotKeyPressed();
            }
        }
        #endregion

        [Test]
        public void OnHotKeyPressedTest()
        {
            ManualResetEvent mreAction = new ManualResetEvent(false);
            ManualResetEvent mreEvent = new ManualResetEvent(false);

            DerivedHotKey hk = new DerivedHotKey(Key.C, ModifierKeys.Alt, () => mreAction.Set());
            hk.HotKeyPressed += (sender, e) => mreEvent.Set();

            hk.RaiseHKPress();
            bool actionraised = mreAction.WaitOne(1000*3);
            bool eventraised = mreEvent.WaitOne(1000*3);

            if ( ! actionraised )
                Assert.Fail("Action callback not raised.");
            if (! eventraised )
                Assert.Fail("Event not raised.");

        }
    }
}
